home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Networking / OTSimpleDownloadHTTP1.0d3 / OTSimpleDownloadHTTP.c < prev    next >
Encoding:
Text File  |  1998-07-09  |  8.5 KB  |  293 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        OTSimpleDownloadHTTP.c
  3.  
  4.     Contains:    Implementation of the simple HTTP download sample.
  5.  
  6.     Written by:    Quinn "The Eskimo!"
  7.  
  8.     Copyright:    © 1997 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.     You may incorporate this sample code into your applications without
  13.     restriction, though the sample code has been provided "AS IS" and the
  14.     responsibility for its operation is 100% yours.  However, what you are
  15.     not permitted to do is to redistribute the source as "DSC Sample Code"
  16.     after having made changes. If you're going to re-distribute the source,
  17.     we require that you make it clear in the source that the code was
  18.     descended from Apple Sample Code, but that you've made changes.
  19. */
  20.  
  21. /////////////////////////////////////////////////////////////////////
  22. // The OT debugging macros in <OTDebug.h> require this variable to
  23. // be set.
  24.  
  25. #ifndef qDebug
  26. #define qDebug    1
  27. #endif
  28.  
  29. /////////////////////////////////////////////////////////////////////
  30. // Pick up all the standard OT stuff.
  31.  
  32. #include <OpenTransport.h>
  33.  
  34. /////////////////////////////////////////////////////////////////////
  35. // Pick up all the OT TCP/IP stuff.
  36.  
  37. #include <OpenTptInternet.h>
  38.  
  39. /////////////////////////////////////////////////////////////////////
  40. // Pick up the OTDebugBreak and OTAssert macros.
  41.  
  42. #include <OTDebug.h>
  43.  
  44. /////////////////////////////////////////////////////////////////////
  45. // Pick up YieldToAnyThread.
  46.  
  47. #include <Threads.h>
  48.  
  49. /////////////////////////////////////////////////////////////////////
  50. // Pick up our own prototype.
  51.  
  52. #include "OTSimpleDownloadHTTP.h"
  53.  
  54. /////////////////////////////////////////////////////////////////////
  55. // OTDebugStr is not defined in any OT header files, but it is
  56. // exported by the libraries, so we define the prototype here.
  57.  
  58. extern pascal void OTDebugStr(const char* str);
  59.  
  60. /////////////////////////////////////////////////////////////////////
  61.  
  62. enum {
  63.     kTransferBufferSize = 4096
  64. };
  65.  
  66. /////////////////////////////////////////////////////////////////////
  67.  
  68. static pascal void YieldingNotifier(void* contextPtr, OTEventCode code, 
  69.                                        OTResult result, void* cookie)
  70.     // This simple notifier checks for kOTSyncIdleEvent and
  71.     // when it gets one calls the Thread Manager routine
  72.     // YieldToAnyThread.  Open Transport sends kOTSyncIdleEvent
  73.     // whenever it's waiting for something, eg data to arrive
  74.     // inside a sync/blocking OTRcv call.  In such cases, we
  75.     // yield the processor to some other thread that might
  76.     // be doing useful work.
  77. {
  78.     #pragma unused(contextPtr)
  79.     #pragma unused(result)
  80.     #pragma unused(cookie)
  81.     OSStatus junk;
  82.     
  83.     switch (code) {
  84.         case kOTSyncIdleEvent:
  85.             junk = YieldToAnyThread();
  86.             OTAssert("YieldingNotifier: YieldToAnyThread failed", junk == noErr);
  87.             break;
  88.         default:
  89.             // do nothing
  90.             break;
  91.     }
  92. }
  93.  
  94. /////////////////////////////////////////////////////////////////////
  95.  
  96. OSStatus DownloadHTTPSimple(const char *hostName,
  97.                             const char *httpCommand,
  98.                             const short destFileRefNum)
  99.     // Download a URL from the a web server.  hostName is a pointer
  100.     // to a string that contains the DNS address of the web server.
  101.     // The DNS address must be suffixed by ":<port>", where <port>
  102.     // is the port number the web server is operating on.
  103.     // httpCommand contains the HTTP command to send.  Typically this
  104.     // is of the form:
  105.     //
  106.     //        GET <x> HTTP/1.0\0x0d\0x0a\0x0d\0x0a
  107.     //
  108.     // where <x> is the URL path.  destFileRefNum is the file
  109.     // reference number to which the results of the HTTP command
  110.     // are written.  This routine does not parse the returned HTTP
  111.     // header in any way.  The entire incoming stream goes into
  112.     // the file verbatim.
  113.     //
  114.     // For example, if you were asked to download a URL like:
  115.     //
  116.     //        http://devworld.apple.com/dev/technotes.shtml
  117.     //
  118.     // you would set:
  119.     //
  120.     //         o hostName to "devworld.apple.com:80" (80 is the
  121.     //          default port for HTTP.
  122.     //        o httpCommand to "GET /dev/technotes.shtml HTTP/1.0\0x0d\0x0a\0x0d\0x0a"
  123. {
  124.     OSStatus     err;
  125.     OSStatus     junk;
  126.     Ptr            transferBuffer     = nil;
  127.     EndpointRef ep                 = kOTInvalidEndpointRef;
  128.     TCall         sndCall;
  129.     DNSAddress     hostDNSAddress;
  130.     OTFlags     junkFlags;
  131.     OTResult     bytesSent;
  132.     OTResult     bytesReceived;
  133.     OTResult     lookResult;
  134.     Boolean        bound            = false;
  135.     
  136.     // First allocate a buffer for storing the data as we read it.
  137.     
  138.     err = noErr;
  139.     transferBuffer = OTAllocMem(kTransferBufferSize);
  140.     if ( transferBuffer == nil ) {
  141.         err = kENOMEMErr;
  142.     }
  143.     
  144.     // Now open a TCP endpoint.
  145.     
  146.     if (err == noErr) {
  147.         ep = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, nil, &err);
  148.     }
  149.     
  150.     // If the endpoint opens successfully...
  151.     
  152.     if (err == noErr) {
  153.  
  154.         // Establish the modes of operation.  This sample uses
  155.         // sync/blocking mode, with sync idle events that yield
  156.         // time using the Thread Manager.
  157.  
  158.         junk = OTSetSynchronous(ep);
  159.         OTAssert("DownloadHTTPSimple: OTSetSynchronous failed", junk == noErr);
  160.         
  161.         junk = OTSetBlocking(ep);
  162.         OTAssert("DownloadHTTPSimple: OTSetBlocking failed", junk == noErr);
  163.         
  164.         junk = OTInstallNotifier(ep, YieldingNotifier, nil);
  165.         OTAssert("DownloadHTTPSimple: OTInstallNotifier failed", junk == noErr);
  166.         
  167.         junk = OTUseSyncIdleEvents(ep, true);
  168.         OTAssert("DownloadHTTPSimple: OTUseSyncIdleEvents failed", junk == noErr);
  169.  
  170.         // Bind the endpoint.  Because we're an outgoing connection,
  171.         // we don't have to bind it to a specific address.
  172.                 
  173.         err = OTBind(ep, nil, nil);
  174.         bound = (err == noErr);
  175.     }
  176.         
  177.     // Initialise the sndCall structure and call OTConnect.  We nil
  178.     // out most of the fields in the sndCall structure because
  179.     // we don't want any special options or connection data.
  180.     // The important field of the sndCall is the addr TNetBuf,
  181.     // which we initialise to the
  182.     
  183.     if (err == noErr) {
  184.         sndCall.addr.buf     = (UInt8 *) &hostDNSAddress;
  185.         sndCall.addr.len     = OTInitDNSAddress(&hostDNSAddress, (char *) hostName);
  186.         sndCall.opt.buf     = nil;        // no connection options
  187.         sndCall.opt.len     = 0;
  188.         sndCall.udata.buf     = nil;        // no connection data
  189.         sndCall.udata.len     = 0;
  190.         sndCall.sequence     = 0;        // ignored by OTConnect
  191.         
  192.         err = OTConnect(ep, &sndCall, nil);
  193.     }
  194.     
  195.     // Send the HTTP command to the server.
  196.     
  197.     if (err == noErr) {
  198.         bytesSent = OTSnd(ep, (void *) httpCommand, OTStrLength(httpCommand), 0);
  199.         
  200.         // OTSnd returns the number of bytes sent.  Because we're in
  201.         // synchronous mode, it won't return until it's sent all the
  202.         // bytes, or it gets an error.
  203.         
  204.         if (bytesSent > 0) {
  205.             err = noErr;
  206.         } else {
  207.             err = bytesSent;
  208.         }
  209.     }
  210.     
  211.     // Now that we have sent the HTTP command, we turn around and
  212.     // receive the data comming back from the server.
  213.     
  214.     if (err == noErr) {
  215.         do {
  216.             bytesReceived = OTRcv(ep, (void *) transferBuffer, kTransferBufferSize, &junkFlags);
  217.             
  218.             // OTRcv returns the number of bytes received.  Because we're in
  219.             // synchronous mode, it won't return until it's sent all the
  220.             // bytes, or it gets an error.  
  221.             
  222.             if (bytesReceived > 0) {
  223.                 err = FSWrite(destFileRefNum, &bytesReceived, transferBuffer);
  224.             } else {
  225.                 err = bytesReceived;
  226.             }
  227.             
  228.             // We keep running this loop until we get an error.
  229.     
  230.         } while (err == noErr);
  231.     }
  232.  
  233.     // Now we handle the various forms of error we can get.  The
  234.     // most common in kOTLookErr.  This means that some event
  235.     // has happened that we need to look at.  We call OTLook
  236.     // to get the event code and then handle the various types
  237.     // of event appropriately.
  238.     
  239.     if (err == kOTLookErr) {
  240.  
  241.         lookResult = OTLook(ep);
  242.  
  243.         switch (lookResult) {
  244.  
  245.             case T_DISCONNECT:
  246.                 // If we get a T_DISCONNECT event, the remote peer
  247.                 // has disconnected the stream in a dis-orderly
  248.                 // fashion.  HTTP servers will often just disconnect
  249.                 // a connection like this to indicate the end of the
  250.                 // data, so all we need do is clear the T_DISCONNECT
  251.                 // event on the endpoint.
  252.  
  253.                 err = OTRcvDisconnect(ep, nil);
  254.                 break;
  255.                 
  256.             case T_ORDREL:
  257.                 // If we get a T_ORDREL event, the remote peer
  258.                 // has disconnected the stream in an orderly
  259.                 // fashion.  This orderly disconnect indicates that
  260.                 // the end of the data.  We respond by clearing
  261.                 // the T_ORDREL, and then calling OTSndOrderlyDisconnect
  262.                 // to acknowledge the orderly disconnect at
  263.                 // the remote peer.
  264.                 
  265.                 err = OTRcvOrderlyDisconnect(ep);
  266.                 if (err == noErr) {
  267.                     err = OTSndOrderlyDisconnect(ep);
  268.                 }
  269.                 break;
  270.                 
  271.             default:
  272.                 // Leave err as kOTLookErr.
  273.                 break;
  274.         }
  275.     }
  276.  
  277.     if ( (err == noErr) && bound ) {
  278.         junk = OTUnbind(ep);
  279.         OTAssert("DownloadHTTPSimple: OTUnbind failed.", junk == noErr);
  280.     }
  281.     
  282.     // Clean up.
  283.     if (ep != kOTInvalidEndpointRef) {
  284.         junk = OTCloseProvider(ep);
  285.         OTAssert("DownloadHTTPSimple: OTCloseProvider failed.", junk == noErr);
  286.     }
  287.     if (transferBuffer != nil) {
  288.         OTFreeMem(transferBuffer);
  289.     }
  290.     
  291.     return (err);
  292. }
  293.